#ifndef LINKEDLISTCORE_H_INCLUDED
#define LINKEDLISTCORE_H_INCLUDED

#include "Constants.h"
#include "MemoryManager.h"

namespace CEngine {

template<class T>
LinkedList<T>::LinkedList(void)
    :   root(),
        size(0),
        possessive(true)
{}

template<class T>
LinkedList<T>::LinkedList(const U64 n, const bool p)
    :   root(),
        size(n),
        possessive(p)
{
    if(n == 0) {
        return;
    }

    Link* current,* previous;
    current = &root;

    for (U64 i = 0; i < n; ++i) {
        previous = current;
        current = MemoryManager::alloc(Link());

        current->previous = previous;
        previous->next = current;
        current->element = MemoryManager::alloc(T());
    }
    current->next = &root;
    root.previous = current;
}

template<class T>
LinkedList<T>::LinkedList(const U64 n, const T& value, const bool p)
    :   root(),
        size(n),
        possessive(p)
{
    if(n == 0) {
        return;
    }

    Link* current,* previous;

    current = &root;

    for (U64 i = 0; i < n; ++i) {
        previous = current;
        current = MemoryManager::alloc(Link());

        current->previous = previous;
        previous->next = current;
        current->element = (T*)MemoryManager::rawAlloc(sizeof(T));
        *current->element = value;
    }
    current->next = &root;
    root.previous = current;
}

template<class T>
template<class I>
LinkedList<T>::LinkedList(const Iterator<I>& first, const Iterator<I>& last, const bool p)
    :   root(),
        possessive(p)
{
    ASSERT(first < last);

    size = last.getPosition() - first.getPosition();

    Link* current,* previous;
    current = &root;


    for (Iterator<I> it = first; it < last; ++it) {
        previous = current;
        current = MemoryManager::alloc(Link());

        current->previous = previous;
        previous->next = current;

        if(possessive) {
            current->element = (T*)MemoryManager::rawAlloc(sizeof(T));
            *current->element = (*it);
        }
        else {
            current->element = &(*it);
        }
    }

    current->next = &root;
    root.previous = current;
}

template<class T>
LinkedList<T>::LinkedList(const LinkedList<T>& list)
    :   root(),
        size(list.size),
        possessive(list.possessive)
{
    if(size == 0) {
        return;
    }

    Link* current,* previous,* listLink;
    current = &root;
    listLink = const_cast<Link*>(&list.root);

    for (U64 i = 0; i < size; ++i) {
        previous = current;
        current = MemoryManager::alloc(Link());
        listLink = listLink->next;

        current->previous = previous;
        previous->next = current;

        if(possessive) {
            current->element = (T*)MemoryManager::rawAlloc(sizeof(T));
            *current->element = *listLink->element;
        }
        else {
            current->element = listLink->element;
        }
    }

    current->next = &root;
    root.previous = current;
}

template<class T>
LinkedList<T>::LinkedList(const LinkedList<T>& list, const bool p)
    :   root(),
        size(list.size),
        possessive(p)
{
    if(size == 0) {
        return;
    }

    Link* current,* previous,* listLink;

    current = &root;
    listLink = &list.root;

    for (U64 i = 0; i < size; ++i) {
        previous = current;
        current = MemoryManager::alloc(Link());
        listLink = listLink.next;

        current->previous = previous;
        previous->next = current;

        if(possessive) {
            current->element = (T*)MemoryManager::rawAlloc(sizeof(T));
            *current->element = *listLink.element;
        }
        else {
            current->element = listLink.element;
        }
    }

    current->next = &root;
    root.previous = current;
}

template<class T>
LinkedList<T>&
LinkedList<T>::operator=(const LinkedList& list) {
    if(this == &list) {
        return *this;
    }

    Link* current = root.next;

    if(possessive) {
        while(current != &root) {
            current = current->next;
            MemoryManager::free(current->previous->element);
            MemoryManager::free(current->previous);
        }
    }
    else {
        while(current != &root) {
            current = current->next;
            MemoryManager::free(current->previous);
        }
    }

    Link* previous,* listLink;

    current = &root;
    listLink = &list.root;
    size = list.size;

    for (U64 i = 0; i < size; ++i) {
        previous = current;
        current = MemoryManager::alloc(Link());
        listLink = listLink.next;

        current->previous = previous;
        previous->next = current;

        if(possessive) {
            current->element = (T*)MemoryManager::rawAlloc(sizeof(T));
            *current->element = *listLink.element;
        }
        else {
            current->element = listLink.element;
        }
    }

    current->next = &root;
    root.previous = current;

    return *this;
}

template<class T>
LinkedList<T>::~LinkedList(void) {
    if(size == 0) {
        return;
    }

    Link* current = root.next;
    if(possessive) {
        while(current != &root) {
            current = current->next;
            MemoryManager::free(current->previous->element);
            MemoryManager::free(current->previous);
        }
    }
    else {
        while(current != &root) {
            current = current->next;
            MemoryManager::free(current->previous);
        }
    }
    root.next = NULL;
    root.previous = NULL;
}

template<class T>
typename LinkedList<T>::ListIterator LinkedList<T>::begin(void) const {
    ListIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<LinkedList<T>*>(this);
    it.currentPointer = root.next;

    return it;
}

template<class T>
typename LinkedList<T>::ListIterator LinkedList<T>::rBegin(void) const {
    ListIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<LinkedList<T>*>(this);
    it.currentPointer = root.previous;
    it.position = size - 1;

    return it;
}

template<class T>
typename LinkedList<T>::ListIterator LinkedList<T>::end(void) const {
    ListIterator it = {0};
    if(size == 0) {
        return it;
    }

    it.basePointer = const_cast<LinkedList<T>*>(this);
    it.currentPointer = const_cast<Link*>(&root);
    it.position = size;

    return it;
}

template<class T>
T& LinkedList<T>::front(void) const{
    if(size == 0) {
        defaultLog.log(CEngine::Logging::CONTAINERS, LOG_ERROR, "Accessed element in empty container!\n");
    }

    return *root.next->element;
}

template<class T>
T& LinkedList<T>::back(void) const{
    if(size == 0) {
        defaultLog.log(CEngine::Logging::CONTAINERS, LOG_ERROR, "Accessed element in empty container!\n");
    }

    return *root.previous->element;
}

template<class T>
void LinkedList<T>::pushFront(const T& element) {
    Link* link = MemoryManager::alloc(Link());

    link->element = const_cast<T*>(&element);

    if(size == 0) {
        root.previous = link;
        root.next = link;

        link->previous = &root;
        link->next = &root;
    }
    else {
        root.next->previous = link;
        link->next = root.next;

        link->previous = &root;
        root.next = link;
    }

    ++size;
}

template<class T>
void LinkedList<T>::popFront(void) {
    if(size == 0) {
        return;
    }

    Link* link = root.next;

    root.next = root.next->next;
    root.next->previous = &root;

    if(possessive) {
        MemoryManager::free(link->element);
        MemoryManager::free(link);
    }
    else {
        MemoryManager::free(link);
    }

    --size;
}

template<class T>
void LinkedList<T>::pushBack(const T& element) {
    Link* link = MemoryManager::alloc(Link());

    link->element = const_cast<T*>(&element);

    if(size == 0) {
        root.previous = link;
        root.next = link;

        link->previous = &root;
        link->next = &root;
    }
    else {
        root.previous->next = link;
        link->previous = root.previous;

        link->next = &root;
        root.previous = link;
    }

    ++size;
}

template<class T>
void LinkedList<T>::popBack(void) {
    if(size == 0) {
        return;
    }

    Link* link = root.previous;

    root.previous = root.previous->previous;
    root.previous->next = &root;

    if(possessive) {
        MemoryManager::free(link->element);
        MemoryManager::free(link);
    }
    else {
        MemoryManager::free(link);
    }

    --size;
}

template<class T>
void LinkedList<T>::insert(const Iterator<T>& p, const T& element) {
    Iterator<T> position = p;

    ListIterator it;
    it.basePointer = this;
    it.currentPointer = &root;
    it.positon = size;

    if(position == it) {
        --position;
    }

    Link* link = MemoryManager::alloc(Link());

    link->element = const_cast<T*>(&element);

    Link* temp = &traverseList(position.getPosition());

    temp->previous->next = link;
    link->previous = temp->previous;

    link->next = temp;
    temp->previous = link;

    ++size;
}

template<class T>
void LinkedList<T>::insert(const Iterator<T>& p, const U64 noe, const T& element) {
    Iterator<T> position = p;

    ListIterator it;
    it.basePointer = this;
    it.currentPointer = &root;
    it.positon = size;

    if(position == it) {
        --position;
    }

    U64 numberOfElements = noe;
    if(!possessive && numberOfElements != 0) {
        numberOfElements = 1;
    }

    Link* end = &position;
    Link* start = end->previous;
    Link* link,* temp = start;

    for (U64 i = 0; i < numberOfElements; ++i) {
        link = MemoryManager::alloc(Link());

        if(possessive) {
            link->element = (T*)MemoryManager::rawAlloc(sizeof(T));
            *link->element = element;
        }
        else {
            link->element = const_cast<T*>(&element);
        }

        temp->next = link;
        link->previous = temp;

        temp = link;
    }
    link->next = end;
    end->previous = link;

    size += numberOfElements;
}

template<class T>
void LinkedList<T>::erase(const Iterator<T>& position) {
    Link* link = &position;

    link->previous->next = link->next;
    link->next->previous = link->previous;

    if(possessive) {
        MemoryManager::free(link->element);
        MemoryManager::free(link);
    }
    else {
        MemoryManager::free(link);
    }

    --size;
}

template<class T>
void LinkedList<T>::erase(const Iterator<T>& first, const Iterator<T>& l) {
    U64 last = &l;
    if(size == 0 || first > last) {
        return;
    }
    if(last == size) {
        --last;
    }

    Link* start = &first;
    Link* end = &last;
    Link* link = start;

    start->previous->next = end->next;
    end->next->previous = start->previous;

    for (U64 i = 0; i < ((last + 1) - first); ++i) {
        Link* temp = link->next;

        if(possessive) {
            MemoryManager::free(link->element);
            MemoryManager::free(link);
        }
        else {
            MemoryManager::free(link);
        }

        link = temp;
    }

    size -= ((last + 1) - first);
}

template<class T>
void LinkedList<T>::resize(const U64 n) {
    if(n < size) {
        Link* newEnd = &traverseList(n - 1);
        Link* link = newEnd->next;
        newEnd->next = &root;
        root.previous = newEnd;

        for (U64 i = 0; i < (size - n); ++i) {
            Link* temp = link->next;

            if(possessive) {
                MemoryManager::free(link->element);
                MemoryManager::free(link);
            }
            else {
                MemoryManager::free(link);
            }

            link = temp;
        }

        size = n;
    }
    else if (n > size && possessive) {
        Link* link = root.previous;

        for (U64 i = 0; i < (n - size); ++i) {
            Link* newLink = MemoryManager::alloc(Link());

            newLink->element = MemoryManager::alloc(T());

            newLink->previous = link;
            link->next = newLink;

            link = newLink;
        }
        link->next = &root;
        root.previous = link;

        size = n;
    }
}

template<class T>
void LinkedList<T>::resize(const U64 n, const T& element) {
    if(n < size) {
        Link* newEnd = &traverseList(n - 1);
        Link* link = newEnd->next;
        newEnd->next = &root;
        root.previous = newEnd;

        for (U64 i = 0; i < (size - n); ++i) {
            Link* temp = link->next;

            if(possessive) {
                MemoryManager::free(link->element);
                MemoryManager::free(link);
            }
            else {
                MemoryManager::free(link);
            }

            link = temp;
        }

        size = n;
    }
    else if (n > size && possessive) {
        Link* link = root.previous;

        for (U64 i = 0; i < (n - size); ++i) {
            Link* newLink = MemoryManager::alloc(Link());

            newLink->element = (T*)MemoryManager::rawAlloc(sizeof(T));
            *newLink->element = element;

            newLink->previous = link;
            link->next = newLink;

            link = newLink;
        }
        link->next = &root;
        root.previous = link;

        size = n;
    }
}

template<class T>
void LinkedList<T>::clear(void) {
    if(size == 0) {
        return;
    }

    size = 0;

    Link* current = root.next;
    if(possessive) {
        while(current != &root) {
            current = current->next;
            MemoryManager::free(current->previous->element);
            MemoryManager::free(current->previous);
        }
    }
    else {
        while(current != &root) {
            current = current->next;
            MemoryManager::free(current->previous);
        }
    }

    root.next = NULL;
    root.previous = NULL;
}

template<class T> inline
bool LinkedList<T>::isEmpty(void) const {
    if(size == 0) {
        return true;
    }
    return false;
}

template<class T> inline
U64 LinkedList<T>::getSize(void) const {
    return size;
}

template<class T> inline
bool LinkedList<T>::isPossessive(void) const {
    return possessive;
}

template<class T> inline
void LinkedList<T>::setPossessive(const bool p) {
    possessive = p;
}

template<class T>
auto LinkedList<T>::traverseList(const U64 position, Iterator<T>* current) const -> LinkedList<T>::Link& {
    Link* link;

    U64 fromBeginning = position;
    U64 fromEnd = (size - 1) - position;
    I64 fromCurrent;
    if(current != NULL) {
        fromCurrent = position - current->getPosition();
    }
    else {
        fromCurrent = INT64_MAX;
    }

    if(fromBeginning <= abs(fromCurrent) && fromBeginning <= fromEnd) {
        link = root.next;
        for (U64 i = fromBeginning; i != 0; --i) {
            link = link->next;
        }
    }
    else if(fromEnd <= abs(fromCurrent)) {
        link = root.previous;
        for (U64 i = fromEnd; i != 0; --i) {
            link = link->previous;
        }
    }
    else {
        link = current->iteratorData.list.currentElement;
        if(fromCurrent < 0) {
            for (U64 i = abs(fromCurrent); i != 0; --i) {
                link = link->previous;
            }
        }
        else {
            for (U64 i = fromCurrent; i != 0; --i) {
                link = link->next;
            }
        }
    }

    return *link;
}

}

#endif // LINKEDLISTCORE_H_INCLUDED
